package cn.xiuxius.embedded.toolwindow;

import com.fazecast.jSerialComm.SerialPort;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.Messages;
import com.intellij.ui.components.*;
import com.intellij.util.ui.JBUI;

import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;

public class SerialToolWindow {

    private final JBPanel<?> mainPanel;
    private final ComboBox<String> portComboBox;
    private final JButton openButton;
    private final JButton refreshButton;
    private final ComboBox<String> baudRateCombo;
    private final ComboBox<String> dataBitsCombo;
    private final ComboBox<String> parityCombo;
    private final ComboBox<String> stopBitsCombo;
    private final JBPanel<?> terminalContainer;
    private final List<SerialTerminalPanel> terminals = new ArrayList<>();

    public SerialToolWindow() {
        mainPanel = new JBPanel<>(new BorderLayout());

        // Top config panel with GridBagLayout for neat 2-column layout
        JBPanel<?> configPanel = new JBPanel<>(new GridBagLayout());
        configPanel.setBorder(JBUI.Borders.empty(10,10,10,10));

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = JBUI.insets(5, 5, 10, 5);  // 上5 下10 左右5，增加行间距
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.weightx = 0;

        // Helper to add label + combo in one row, max 2 per row
        final int[] gridx = {0};
        final int[] gridy = {0};

        // Add method for label + combo
        java.util.function.BiConsumer<String, JComponent> addLabelAndComp = (labelText, comp) -> {
            gbc.gridx = gridx[0]++;
            gbc.gridy = gridy[0];
            gbc.weightx = 0;
            gbc.anchor = GridBagConstraints.LINE_END;
            configPanel.add(new JBLabel(labelText), gbc);

            gbc.gridx = gridx[0]++;
            gbc.weightx = 1;
            gbc.anchor = GridBagConstraints.LINE_START;
            configPanel.add(comp, gbc);

            if (gridx[0] >= 4) {  // 两个标签+控件完一行，重置
                gridx[0] = 0;
                gridy[0]++;
            }
        };

        baudRateCombo = new ComboBox<>(new String[]{
                "1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200", "自定义"
        });
        baudRateCombo.setSelectedItem("115200");

        dataBitsCombo = new ComboBox<>(new String[]{"5", "6", "7", "8"});
        dataBitsCombo.setSelectedItem("8");

        parityCombo = new ComboBox<>(new String[]{"无", "奇校验", "偶校验", "标记校验", "空校验"});
        parityCombo.setSelectedIndex(0);

        stopBitsCombo = new ComboBox<>(new String[]{"1", "1.5", "2"});
        stopBitsCombo.setSelectedItem("1");

        addLabelAndComp.accept("波特率:", baudRateCombo);
        addLabelAndComp.accept("数据位:", dataBitsCombo);
        addLabelAndComp.accept("校验位:", parityCombo);
        addLabelAndComp.accept("停止位:", stopBitsCombo);

        gbc.gridx = 0;
        gbc.gridy = gridy[0]++;
        gbc.gridwidth = 1;
        gbc.weightx = 0;
        gbc.anchor = GridBagConstraints.LINE_END;
        gbc.insets = JBUI.insets(5, 5, 10, 5);

        gbc.gridx = 1;
        gbc.gridwidth = 1;
        gbc.weightx = 0.3;
        gbc.anchor = GridBagConstraints.LINE_START;

        // Port select panel with GridBagLayout, 2 buttons per line
        JBPanel<?> portPanel = new JBPanel<>(new GridBagLayout());
        portPanel.setBorder(JBUI.Borders.empty(10,10,10,10));
        GridBagConstraints gbcPort = new GridBagConstraints();
        gbcPort.insets = JBUI.insets(5, 5, 10, 5);
        gbcPort.fill = GridBagConstraints.HORIZONTAL;
        gbcPort.weightx = 0;
        gbcPort.gridy = 0;

        gbcPort.gridx = 0;
        gbcPort.anchor = GridBagConstraints.LINE_END;
        portPanel.add(new JBLabel("串口:"), gbcPort);

        portComboBox = new ComboBox<>();
        gbcPort.gridx = 1;
        gbcPort.weightx = 1;
        gbcPort.anchor = GridBagConstraints.LINE_START;
        portPanel.add(portComboBox, gbcPort);

        openButton = new JButton("打开串口");
        gbcPort.gridx = 2;
        gbcPort.weightx = 0;
        gbcPort.anchor = GridBagConstraints.CENTER;
        portPanel.add(openButton, gbcPort);

        refreshButton = new JButton("刷新串口");
        gbcPort.gridx = 3;
        portPanel.add(refreshButton, gbcPort);

        // If need to limit max 2 buttons per row, we can break line here
        // But 2 buttons are in same row, looks fine.

        JBPanel<?> topPanel = new JBPanel<>(new BorderLayout());
        topPanel.add(configPanel, BorderLayout.NORTH);
        topPanel.add(portPanel, BorderLayout.SOUTH);

        mainPanel.add(topPanel, BorderLayout.NORTH);

        terminalContainer = new JBPanel<>(new GridLayout(1, 1, 5, 5));
        mainPanel.add(new JBScrollPane(terminalContainer), BorderLayout.CENTER);

        refreshPorts();
        refreshButton.addActionListener(e -> refreshPorts());
        openButton.addActionListener(e -> openSerialTerminal());
    }

    public JBPanel<?> getMainPanel() {
        return mainPanel;
    }

    private void refreshPorts() {
        portComboBox.removeAllItems();
        for (SerialPort port : SerialPort.getCommPorts()) {
            portComboBox.addItem(port.getSystemPortName());
        }
    }

    private void openSerialTerminal() {
        String portName = (String) portComboBox.getSelectedItem();
        if (portName == null || portName.isEmpty()) {
            Messages.showWarningDialog("请选择串口", "提示");
            return;
        }

        int baudRate;
        try {
            String brStr = (String) baudRateCombo.getSelectedItem();
            if ("自定义".equals(brStr)) {
                String input = Messages.showInputDialog("请输入自定义波特率:", "自定义波特率", null);
                if (input == null || input.isEmpty()) return;
                baudRate = Integer.parseInt(input);
            } else {
                baudRate = Integer.parseInt(brStr);
            }
        } catch (Exception ex) {
            Messages.showErrorDialog("波特率设置无效", "错误");
            return;
        }

        int dataBits = Integer.parseInt((String) dataBitsCombo.getSelectedItem());

        int parity = SerialPort.NO_PARITY;
        switch (parityCombo.getSelectedIndex()) {
            case 1 -> parity = SerialPort.ODD_PARITY;
            case 2 -> parity = SerialPort.EVEN_PARITY;
            case 3 -> parity = SerialPort.MARK_PARITY;
            case 4 -> parity = SerialPort.SPACE_PARITY;
        }

        int stopBits = SerialPort.ONE_STOP_BIT;
        switch (stopBitsCombo.getSelectedIndex()) {
            case 1 -> stopBits = SerialPort.ONE_POINT_FIVE_STOP_BITS;
            case 2 -> stopBits = SerialPort.TWO_STOP_BITS;
        }

        final SerialTerminalPanel[] terminalHolder = new SerialTerminalPanel[1];

        SerialTerminalPanel terminal = new SerialTerminalPanel(
                portName, baudRate, dataBits, parity, stopBits,
                () -> {
                    terminals.remove(terminalHolder[0]);
                    terminalContainer.remove(terminalHolder[0].getPanel());
                    updateTerminalLayout();
                    terminalContainer.revalidate();
                    terminalContainer.repaint();
                }
        );

        terminalHolder[0] = terminal;

        if (terminal.isOpen()) {
            terminals.add(terminal);
            terminalContainer.add(terminal.getPanel());
            updateTerminalLayout();
            terminalContainer.revalidate();
            terminalContainer.repaint();
        }
    }

    private void updateTerminalLayout() {
        int count = Math.max(1, terminals.size());
        terminalContainer.setLayout(new GridLayout(1, count, 5, 5));
    }
}
